package com.qingzhuge.manager.security.filter;

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.qingzhuge.manager.security.token.EnumTokenType;
import com.qingzhuge.manager.security.token.MobileAuthenticationToken;
import com.qingzhuge.manager.security.token.UsernameAuthenticationToken;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.util.Assert;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;

/**
 * @author zeroxiao
 * @date 2019/9/11 21:54
 */
public class JsonAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
    private static final String SPRING_SECURITY_FORM_TYPE_KEY = "type";
    private static final String SPRING_SECURITY_FORM_CODE_KEY = "code";

    private String typeParameter = SPRING_SECURITY_FORM_TYPE_KEY;
    private String codeParameter = SPRING_SECURITY_FORM_CODE_KEY;

    private boolean postOnly = true;

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        if (postOnly && !"POST".equals(request.getMethod())) {
            throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
        }
        String username = "";
        String password = "";
        String type = "";
        String code = "";
        //attempt Authentication when Content-Type is json
        if (request.getContentType().equalsIgnoreCase(MediaType.APPLICATION_JSON_UTF8_VALUE)
                || request.getContentType().equalsIgnoreCase(MediaType.APPLICATION_JSON_VALUE)) {
            //use jackson to deserialize json
            try (InputStream is = request.getInputStream()) {
                JsonParser parser = new JsonFactory().createParser(is);
                JsonToken to;
                while ((to = parser.nextToken()) != JsonToken.END_OBJECT) {
                    if (JsonToken.VALUE_STRING.equals(to)) {
                        String currentName = parser.getCurrentName();
                        if (getUsernameParameter().equals(currentName)) {
                            username = parser.getText();
                        } else if (getPasswordParameter().equals(currentName)) {
                            password = parser.getText();
                        } else if (getCodeParameter().equals(currentName)) {
                            code = parser.getText();
                        } else if (getTypeParameter().equals(currentName)) {
                            type = parser.getText();
                        }
                    }
                }
            } catch (IOException ignored) {
                logger.debug("数据解析错误");
            }
        } else {
            username = obtainUsername(request);
            password = obtainPassword(request);
            type = obtainType(request);
            code = obtainCode(request);
            username = username.trim();
        }
        if (username == null) {
            username = "";
        }
        if (password == null) {
            password = "";
        }
        if (type == null) {
            type = "";
        }
        if (code == null) {
            code = "";
        }
        AbstractAuthenticationToken token;
        EnumTokenType tokenType = EnumTokenType.get(type);
        if (EnumTokenType.mobile.equals(tokenType)) {
            token = new MobileAuthenticationToken(username, password, code);
        }else{
            token = new UsernameAuthenticationToken(username, password, code);
        }
        setDetails(request, token);
        return this.getAuthenticationManager().authenticate(token);
    }

    private String obtainType(HttpServletRequest request) {
        return request.getParameter(typeParameter);
    }

    private String obtainCode(HttpServletRequest request) {
        return request.getParameter(codeParameter);
    }

    private void setDetails(HttpServletRequest request, AbstractAuthenticationToken token) {
        token.setDetails(authenticationDetailsSource.buildDetails(request));
    }

    public void setTypeParameter(String typeParameter) {
        Assert.hasText(typeParameter, "type parameter must not be empty or null");
        this.typeParameter = typeParameter;
    }

    public void setCodeParameter(String codeParameter) {
        Assert.hasText(codeParameter, "code parameter must not be empty or null");
        this.codeParameter = codeParameter;
    }

    @Override
    public void setPostOnly(boolean postOnly) {
        this.postOnly = postOnly;
    }

    public String getTypeParameter() {
        return typeParameter;
    }

    public String getCodeParameter() {
        return codeParameter;
    }
}
